

/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.formserver.viewmodel.common;

import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.operation.BizMgrAction;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationCollection;
import com.inspur.edp.cef.designtime.api.collection.GspEnumValueCollection;
import com.inspur.edp.cef.designtime.api.collection.GspFieldCollection;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspElementObjectType;
import com.inspur.edp.cef.designtime.api.element.GspEnumValue;
import com.inspur.edp.cef.designtime.api.entity.DynamicPropSetInfo;
import com.inspur.edp.cef.designtime.api.util.MetadataUtil;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.formserver.viewmodel.collection.VMActionCollection;
import com.inspur.edp.formserver.viewmodel.collection.ViewObjectCollection;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementMapping;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementSourceType;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoObjectSourceType;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.action.MappedBizAction;
import com.inspur.edp.formserver.viewmodel.action.ViewModelAction;
import com.inspur.edp.formserver.viewmodel.action.ViewModelActionType;
import com.inspur.edp.formserver.viewmodel.DotNetToJavaStringHelper;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewObject;

import com.inspur.edp.lcm.metadata.api.entity.GspMetadata;
import com.inspur.edp.lcm.metadata.api.entity.MetadataCustomizationFilter;
import com.inspur.edp.lcm.metadata.api.service.MetadataRTService;
import com.inspur.edp.lcm.metadata.api.service.RefCommonService;
import com.inspur.edp.metadata.rtcustomization.api.CustomizationService;
import com.inspur.edp.udt.designtime.api.entity.ComplexDataTypeDef;
import com.inspur.edp.udt.designtime.api.entity.UnifiedDataTypeDef;
import io.iec.edp.caf.commons.utils.SpringBeanUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import static com.inspur.edp.cef.designtime.api.element.GspElementObjectType.None;

/**
 * The Tool Of Link Business Entity By View Model
 *
 * @ClassName: LinkBeUtils
 * @Author: Benjamin Gong
 * @Date: 2021/1/11 17:13
 * @Version: V1.0
 */
public class LinkBeUtils {
	private  boolean isRuntime;
	public LinkBeUtils(){

	}
	public LinkBeUtils(boolean isRuntime){
		this.isRuntime=isRuntime;
	}
	private final java.util.HashMap<String, GspBusinessEntity> bizEntitiesDic = new java.util.HashMap<String,
			GspBusinessEntity>();
	private RefCommonService lcmDtService;

	public RefCommonService getLcmDtService(){
	  if(lcmDtService==null)
	    lcmDtService= SpringBeanUtils.getBean(RefCommonService.class);
	  return lcmDtService;
  }

	private CustomizationService customizationService;

	public CustomizationService  getCustomizationService(){
		if(customizationService==null)
			customizationService= SpringBeanUtils.getBean(CustomizationService.class);
		return customizationService;
	}
	private String errorToken = "#GspBefError# ";

	public final void linkWithBe(GspViewModel vm) {
		if (vm.getMainObject().getMapping() == null || vm.getMainObject().getMapping().getSourceType() != GspVoObjectSourceType.BeObject) {
			return;
		}
		// 联动前清空缓存
		bizEntitiesDic.clear();
		// ① 对象
		linkMainObj(vm.getMainObject());
		// ② 操作
		linkActions(vm);
		// 带出字段为枚举时，赋值枚举信息
		linkBeRefElements(vm);
	}

	/**
	 * 关联带出字段联动
	 *
	 * @param vm
	 */
	public final void linkBeRefElements(GspViewModel vm) {
		ArrayList<IGspCommonElement> eles = vm.getAllElementList(true);
		for (IGspCommonElement ele : eles) {
			if (ele.getIsVirtual() || !ele.getIsRefElement()) {
				continue;
			}
			GspViewModelElement viewEle = (GspViewModelElement) ele;
				if (viewEle.getMapping() == null) {
					throw new RuntimeException(String.format("字段Mapping为空，vo名称=%1$s,vo编号=%2$s,字段名称=%3$s", vm.getName(), vm.getCode()
							, viewEle.getName()));
				}
				if (viewEle.getMapping().getSourceType() != GspVoElementSourceType.BeElement) {
					continue;
				}
				if (viewEle.getParentAssociation() == null) {
					throw new RuntimeException(String.format("字段所属关联为空，vo名称=%1$s,vo编号=%2$s,字段名称=%3$s", vm.getName(), vm.getCode()
							, viewEle.getName()));
				}
				GspBizEntityElement bizEle = getRefBizElement(viewEle.getParentAssociation().getRefModelID(), viewEle.getRefElementId());
				if(bizEle == null) {
				    StringBuilder stringBuilder=new StringBuilder("未找到当前VO字段关联的的BE字段，当前VO名称为：");
				    stringBuilder.append(vm.getName()).append(",VO模型id为：").append(vm.getID()).
                        append(",VO字段名称为：").append(viewEle.getName()).append(",字段编号为：").append(viewEle.getCode()).append(",字段ID为：").append(viewEle.getID()).append(",字段关联的BE字段ID为：").append(viewEle.getRefElementId()).
                        append(",被关联模型名称为：").append(viewEle.getParentAssociation().getRefModelName()).append(",关联模型编号为：").
                        append(viewEle.getParentAssociation().getRefModelCode()).append(",关联模型ID为：").
                        append(viewEle.getParentAssociation().getRefModelID());
					throw new RuntimeException(stringBuilder.toString());
				}
				// udt
				viewEle.setIsUdt(bizEle.getIsUdt());
				viewEle.setUdtID(bizEle.getUdtID());
				viewEle.setUdtName(bizEle.getUdtName());
				viewEle.setUdtPkgName(bizEle.getUdtPkgName());
				if (!ele.getIsRefElement() || ele.getObjectType() != GspElementObjectType.Enum) {
					continue;
				}

				if (bizEle.getObjectType() != GspElementObjectType.Enum) {
					StringBuffer strBuffer = new StringBuffer(errorToken);
					strBuffer.append("关联带出字段对象类型为枚举，其对应的关联模型上的该字段不是枚举类型, 当前字段名称为：")
							.append(viewEle.getName())
							.append("字段名称为：")
							.append(viewEle.getCode())
							.append(errorToken);
					throw new RuntimeException(strBuffer.toString());
				}
				// 赋值枚举信息
				viewEle.setContainEnumValues(bizEle.getContainEnumValues());

		}
	}



	public void linkMainObj(GspViewObject viewObj) {
		// 虚拟对象无映射bizObj
		if (viewObj == null || viewObj.getIsVirtual()) {
			throw new RuntimeException(errorToken + "主对象不可为空或虚拟对象。" + errorToken);
		}
		GspBizEntityObject bizObject = getBizObject(viewObj.getMapping(), viewObj.getCode());
		if (bizObject == null) {
			throw new RuntimeException(errorToken + "实体中无vo映射的主对象。" + errorToken);
		}

		linkBizObject(viewObj, bizObject);
	}


	public void linkBizObject(GspViewObject viewObj, GspBizEntityObject bizObject) {
		// ① 基本信息
		linkObjectBasicInfo(viewObj, bizObject);
		// ② 字段集合
		linkElements(viewObj);
		// ③ 字段相关属性
		linkObjectSelfInfo(viewObj, bizObject, viewObj.getParentObject()==null?null:viewObj.getParentObject().getIDElement().getID());
		// ④ 子对象集合
		if (viewObj.getContainChildObjects() != null && viewObj.getContainChildObjects().size() != 0) {
			ViewObjectCollection childObjList = viewObj.getContainChildObjects();
			Iterator<IGspCommonObject> iterators=childObjList.iterator();
			while (iterators.hasNext()){
				GspViewObject childObj= (GspViewObject) iterators.next();
				if (childObj.getIsVirtual()) {
					continue;
				}
				GspBizEntityObject childBizObject = getBizObject(((GspViewObject) childObj).getMapping(), childObj.getCode());
				if (childBizObject == null) {
					iterators.remove();
					continue;
				}
				linkBizObject((GspViewObject) ((GspViewObject) childObj != null ? childObj : null), childBizObject);
			}
		}
	}

	public void linkObjectBasicInfo(GspViewObject viewObj, GspBizEntityObject bizObject) {
		// 若vo节点未修改，则联动be节点编号
		if (DotNetToJavaStringHelper.isNullOrEmpty(viewObj.getCode())) {
			viewObj.setCode(bizObject.getCode());
		}
	}

	public void linkObjectSelfInfo(GspViewObject viewObj, GspBizEntityObject bizObject, String parentObjectElementId) {
		ArrayList<IGspCommonField> elementList = viewObj.getContainElements().getAllItems(item -> item.getIsVirtual() == false);
		HashMap<String, IGspCommonField> viewElements = new HashMap<String, IGspCommonField>();
		for (IGspCommonField item : elementList) {
			viewElements.put(item.getID(), (GspViewModelElement) item);
		}
		// 字段mapping字典
		java.util.HashMap<String, String> elementMappings = ConvertUtils.getElementMappingsDic(elementList);

		// ID生成规则
		ConvertUtils.updateColumnGenerateId(bizObject, viewObj, elementMappings);
	}


	public void linkElements(GspViewObject viewObj) {
		ArrayList<IGspCommonField> elementList = viewObj.getContainElements().getAllItems(item -> item.getIsVirtual() == false);

		// 字段mapping字典
		if (elementList.size() == 0) {
			throw new RuntimeException(errorToken + String.format("当前对象无字段'%1$s'。", viewObj.getName()) + errorToken);
		}
		for (IGspCommonField ele : elementList) {
			GspViewModelElement viewEle = (GspViewModelElement) ele;
			if (viewEle != null) {
				GspBizEntityElement bizElement = getBizElement(viewEle.getMapping(), viewEle.getLabelID());
				if (bizElement == null) {
					viewObj.getContainElements().remove(ele);
					continue;
				}
				linkBizElement(viewEle, bizElement);
				// 兼容旧版mapping的be映射字段
				if (DotNetToJavaStringHelper.isNullOrEmpty(viewEle.getMapping().getTargetElementId())) {
					viewEle.getMapping().setTargetElementId(bizElement.getID());
				}
				if (DotNetToJavaStringHelper.isNullOrEmpty(viewEle.getMapping().getTargetObjectId()) || !viewEle.getMapping().getTargetObjectId() .equals(bizElement.getBelongObject().getID())) {
					viewEle.getMapping().setTargetObjectId(bizElement.getBelongObject().getID());
				}
			}
		}
	}

	public void linkBizElement(GspViewModelElement viewEle, GspBizEntityElement bizEle) {
		linkElementBasicInfo(viewEle, bizEle);
		linkElementObjectType(viewEle, bizEle);
	}

	public void linkElementBasicInfo(GspViewModelElement ele, GspBizEntityElement bizElement) {
		boolean canEditLengthAndPrecision = true;
		if (ele.getMDataType() != bizElement.getMDataType()) {
			ele.setMDataType(bizElement.getMDataType());
			canEditLengthAndPrecision = false;
		}
		ele.setIsMultiLanguage(bizElement.getIsMultiLanguage());

		if (bizElement.getIsRequire()) {
			ele.setIsRequire(bizElement.getIsRequire());
		}
		if (bizElement.getReadonly()) {
			ele.setReadonly(bizElement.getReadonly());
		}

		// udt相关
		ele.setIsUdt(bizElement.getIsUdt());
		ele.setUdtID(bizElement.getUdtID());
		ele.setUdtName(bizElement.getUdtName());
		ele.setUdtPkgName(bizElement.getUdtPkgName());

		GspFieldCollection beChildEles = bizElement.getChildElements();
		GspFieldCollection voChildEles = ele.getChildElements();
		if(beChildEles.size() != voChildEles.size()){
			voChildEles.clear();
			for(IGspCommonField beChildEle : beChildEles){
				GspViewModelElement viewModelElement = ConvertUtils.toElement(
						(IGspCommonElement)beChildEle,
						ele.getMapping().getTargetMetadataPkgName(),
						ele.getMapping().getTargetMetadataId(),
						GspVoElementSourceType.BeElement);
				voChildEles.add(viewModelElement);
			}
		}

		if (bizElement.getIsUdt()) {
			canEditLengthAndPrecision = false;
		} else {
			canEditLengthAndPrecision = true;
		}

		// 长度精度
		linkLengthAndPrecision(ele, bizElement, canEditLengthAndPrecision);


	}

	/**
	 * 更新字段长度精度
	 *
	 * @param ele
	 * @param bizElement
	 * @param canEdit
	 */
	public void linkLengthAndPrecision(GspViewModelElement ele, GspBizEntityElement bizElement, boolean canEdit) {
		if (canEdit) {
			// 长度精度不可大于be字段
			if (ele.getLength() > bizElement.getLength()) {
				ele.setLength(bizElement.getLength());
			}
			if (ele.getPrecision() > bizElement.getPrecision()) {
				ele.setPrecision(bizElement.getPrecision());
			}
		} else {
			ele.setLength(bizElement.getLength());
			ele.setPrecision(bizElement.getPrecision());
		}
	}

	public void linkElementObjectType(GspViewModelElement ele, GspBizEntityElement bizElement) {
		GspViewModelElement transEle = ConvertUtils.toElement(bizElement, null, ele.getMapping().getTargetMetadataId(),GspVoElementSourceType.BeElement);
		ele.setObjectType(bizElement.getObjectType());
		ele.setEnumIndexType(bizElement.getEnumIndexType());
		switch (bizElement.getObjectType()) {
			case Association:
			  if(bizElement.getIsRefElement()) {
			    break;
        }
				linkElementAssos(ele, transEle);
				break;
			case Enum:
				linkElementEnums(ele, transEle);
				break;
			case None:
				break;
			case DynamicProp:
				ele.setDynamicPropSetInfo(bizElement.getDynamicPropSetInfo());
				break;
			default:
				throw new RuntimeException(errorToken + String.format("字段对象类型'{%1$s}'不存在。", bizElement.getObjectType()) + errorToken);
		}
		if (ele.getObjectType() != GspElementObjectType.Association) {
			ele.getChildAssociations().clear();
		}
		if (ele.getObjectType() != GspElementObjectType.Enum) {
			ele.getContainEnumValues().clear();
		}
		if (ele.getObjectType() != GspElementObjectType.DynamicProp) {
			ele.setDynamicPropSetInfo(new DynamicPropSetInfo());
		}

	}

	public void linkElementEnums(GspViewModelElement ele, GspViewModelElement transElement) {
		Object tempVar = ele.getContainEnumValues().clone();
		GspEnumValueCollection voEnumValues = (GspEnumValueCollection) ((GspEnumValueCollection) tempVar != null ? tempVar : null);
		ele.getContainEnumValues().clear();

		for (GspEnumValue enumItem : transElement.getContainEnumValues()) {
			Object tempVar2 = enumItem.clone();
			GspEnumValue enumValue = (GspEnumValue) ((GspEnumValue) tempVar2 != null ? tempVar2 : null);
			if (voEnumValues != null && voEnumValues.size() > 0) {
				for (GspEnumValue voEnumValue : voEnumValues) {
					if (voEnumValue.getValue() .equals( enumValue.getValue())) {
						enumValue.setI18nResourceInfoPrefix(voEnumValue.getI18nResourceInfoPrefix());
					}
				}
			}
			ele.getContainEnumValues().add(enumValue);
		}
	}

	public void linkElementAssos(GspViewModelElement ele, GspViewModelElement transElement) {
		GspAssociationCollection currentAssos = ele.getChildAssociations().clone(ele);
		if (ele.getChildAssociations() != null && ele.getChildAssociations().size() > 0) {
			ele.getChildAssociations().clear();
		}
		if (transElement.getChildAssociations() == null || transElement.getChildAssociations().size() == 0) {
			throw new RuntimeException(errorToken + String.format("当前字段'%1$s'，对象类型为关联，却无关联信息，请联系管理员。", ele.getName()) + errorToken);
		}

		for (GspAssociation transAssociation : transElement.getChildAssociations()) {
			transAssociation.setBelongElement(ele);
			GspAssociation currentAsso = getAssociation(currentAssos, transAssociation.getId());
			if (currentAsso != null && currentAsso.getRefModelID().equals(transAssociation.getRefModelID())) {
				handleRefElementCollection(transAssociation, currentAsso);
				ele.getChildAssociations().add(currentAsso);
			} else {
				ele.getChildAssociations().add(transAssociation);
			}
		}

	}

	/**
	 * 处理已删掉的be关联带出字段
	 *
	 * @param transAssociation
	 * @param currentAsso
	 */
	public void handleRefElementCollection(GspAssociation transAssociation, GspAssociation currentAsso) {
		GspFieldCollection originVoRefElements = currentAsso.getRefElementCollection().clone(currentAsso.getRefElementCollection().getParentObject(), currentAsso);
		currentAsso.getRefElementCollection().clear();
		GspFieldCollection transRefElements = transAssociation.getRefElementCollection();
		for (IGspCommonField refEle : originVoRefElements) {
			GspViewModelElement transRefElement = (GspViewModelElement) transRefElements.getItem(ele -> ele.getRefElementId().equals(refEle.getRefElementId()));
			if (transRefElement == null) {
				continue;
			}
			// 兼容旧的带出字段mapping中无TargetElementID及TargetObjectID问题
			handleEleMapping(transRefElement.getMapping(), ((GspViewModelElement) refEle).getMapping());
			refEle.setIsFromAssoUdt(transRefElement.getIsFromAssoUdt());
			currentAsso.getRefElementCollection().add(refEle);
		}
	}

	public void handleEleMapping(GspVoElementMapping transMapping, GspVoElementMapping eleMapping) {
		// 兼容旧的带出字段mapping中无TargetElementID及TargetObjectID问题
		if (DotNetToJavaStringHelper.isNullOrEmpty(eleMapping.getTargetObjectId())) {
			eleMapping.setTargetObjectId(transMapping.getTargetObjectId());
		}
		if (DotNetToJavaStringHelper.isNullOrEmpty(eleMapping.getTargetElementId())) {
			eleMapping.setTargetElementId(transMapping.getTargetElementId());
		}
		// 若关联带出字段ID已更新
		if (!eleMapping.getTargetElementId().equals(transMapping.getTargetElementId())) {
			eleMapping.setTargetElementId(transMapping.getTargetElementId());
			eleMapping.setTargetObjId(transMapping.getTargetObjId());
		}
	}

	public GspAssociation getAssociation(GspAssociationCollection collection, String associationId) {
		return (GspAssociation) collection.getItem(item -> associationId.equals(item.getId()));
	}

	public GspViewModelElement getElement(GspFieldCollection collection, String labelId) {
		for (IGspCommonField item : collection) {
			if (labelId.equals(item.getLabelID())) {
				return (GspViewModelElement) ((GspViewModelElement) item != null ? item : null);
			}
		}
		return null;
	}




	public void linkActions(GspViewModel vm) {
		ArrayList<ViewModelAction> list = vm.getActions().getAllItems(item -> item.getType() == ViewModelActionType.BEAction);
		VMActionCollection mappedBeActions = new VMActionCollection();
		mappedBeActions.addAll(list);
		GspBusinessEntity be = getBe(vm.getMapping().getTargetMetadataId());
		if (mappedBeActions.size() > 0) {
			for (ViewModelAction action : mappedBeActions) {
				ViewModelMapping mapping=action.getMapping();
				if(mapping==null||mapping.getTargetObjId()==null ||"".equals(mapping.getTargetObjId()))
					continue;
				String actionId=mapping.getTargetObjId();
				BizMgrAction mgrAction = (BizMgrAction) be.getBizMgrActions().getItem(item -> actionId.equals(item.getID()));
				if (mgrAction == null) {
					vm.getActions().remove(action);
					continue;
				}
				MappedBizAction tranAction = ConvertUtils.toMappedAction(mgrAction, action.getMapping().getTargetMetadataId(), null);
				linkWithMgrAction(action, tranAction);
			}
		}
	}

	public void linkWithMgrAction(ViewModelAction targetAction, MappedBizAction sourceAction) {
		targetAction.setCode(sourceAction.getCode());
		targetAction.setName(sourceAction.getName());
		targetAction.getParameterCollection().clear();
		if (sourceAction.getParameterCollection().getCount() > 0)
			for (int i = 0; i < sourceAction.getParameterCollection().getCount(); i++) {
				targetAction.getParameterCollection().add(sourceAction.getParameterCollection().getItem(i));
			}

		targetAction.setReturnValue(sourceAction.getReturnValue());
	}


	public GspBusinessEntity getBe(String id) {
		return getBizEntity(id);
	}

	public GspBizEntityElement getRefBizElement(String refBeId, String refElementId) {
		return getRefBizElement(refBeId, refElementId, true);
	}

	public GspBizEntityElement getRefBizElement(String refBeId, String refElementId, boolean containRef) {
		GspBusinessEntity be = getBizEntity(refBeId);
		ArrayList<IGspCommonElement> list = be.getAllElementList(true);
		for (IGspCommonElement ele : list) {
			if (refElementId.equals(ele.getID())) {
				return (GspBizEntityElement) ele;
			}
		}
		return null;
	}


	public GspBizEntityElement getBizElement(ViewModelMapping mapping) {
		return getBizElement(mapping, "");
	}

	public GspBizEntityElement getBizElement(ViewModelMapping mapping, String elementLabelId) {
		if (mapping == null) {
			throw new RuntimeException(errorToken + String.format("当前字段'%1$s'无mapping信息。", elementLabelId) + errorToken);
		}
		String metadataId = mapping.getTargetMetadataId();
		String objId = mapping.getTargetObjId();
		GspBizEntityElement ele = getRefBizElement(metadataId, objId, false);
		return ele;
	}

	public GspBizEntityObject getBizObject(ViewModelMapping mapping, String objCode) {
		if (mapping == null) {
			throw new RuntimeException(String.format("%1$s当前对象'%2$s'无mapping信息。%1$s", errorToken, objCode));
		}
		String metadataId = mapping.getTargetMetadataId();
		String objId = mapping.getTargetObjId();
		GspBusinessEntity be = getBizEntity(metadataId);
		GspBizEntityObject bizObj = (GspBizEntityObject) be.getNode(node -> node.getID().equals(objId));
		return bizObj;
	}

	//todo:依赖lcm加载元数据服务
	public GspBusinessEntity getBizEntity(String id) {
        if (bizEntitiesDic.containsKey(id)) {
            return bizEntitiesDic.get(id);
        }
        GspMetadata metadata;
        if(isRuntime){
        	//运行时获取，元数据不带有i8n信息
        	metadata= MetadataUtil.getCustomMetadataByI18n(id,false);
		}
        else {
        	//设计时获取
			metadata = getLcmDtService().getRefMetadata(id);
		}
        //todo 需要后续讨论，抛异常时提示出明确的元数据信息，不仅仅提示id
        if(metadata==null) {
        	throw  new RuntimeException(String.format("元数据id为%1$s的元数据未获取到，请检查元数据是否已经部署到环境中。",id));
		}
        GspBusinessEntity entity= (GspBusinessEntity) metadata.getContent();
        bizEntitiesDic.put(id, entity);
        return entity;
	}
}
